﻿using System;
using System.Web.Mvc;
using System.Web.Security;
using CodeStash.Models.Security;
using CodeStash.Services;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId;
using DotNetOpenAuth.OpenId.Extensions.AttributeExchange;
using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
using DotNetOpenAuth.OpenId.RelyingParty;
using System.Linq;

namespace CodeStash.Controllers
{
    public class AccountController : Controller
    {
        private static OpenIdRelyingParty openid = new OpenIdRelyingParty();
        private IFormsAuthenticationService formsService;
        private IMembershipService membershipService;
        private ILoggerService loggerService;

        public AccountController(   IFormsAuthenticationService formsService, 
                                    IMembershipService membershipService,
                                    ILoggerService loggerService)
        {
            this.formsService = formsService;
            this.membershipService = membershipService;
            this.loggerService = loggerService;
        }


       

         /// <summary>
        /// URL: /Account/LogOn
        /// </summary>
        public ActionResult LogOn()
        {
            loggerService.Info("Account : LogOn GET");
            return View();
        }

        [HttpPost]
        [ValidateAntiForgeryToken(Salt = "LogOn")]
        public ActionResult LogOn(LogOnModel model, string returnUrl)
        {

            if (ModelState.IsValid)
            {
                if (membershipService.ValidateUser(model.UserName, model.Password))
                {
                    formsService.SignIn(model.UserName, model.RememberMe);
                    if (Url.IsLocalUrl(returnUrl))
                    {
                        loggerService.Info(string.Format("Account : LogOn, Sucessful redirecting to {0}", returnUrl));
                        return Redirect(returnUrl);
                    }
                    else
                    {
                        Session["EncryptedPasswordForUserToWriteDown"] = null;
                        loggerService.Error("Account : LogOn, UnSuccessful logon redirecting to Home");
                        return RedirectToAction("Index", "Home");
                    }
                }
                else
                {
                    loggerService.Error("Account : LogOn, The user name or password provided is incorrect.");
                    ModelState.AddModelError("", "The user name or password provided is incorrect.");
                }
            }
            else
            {
                string errors = ModelState.Keys.Aggregate((x, y) => string.Format("{0}\r\n{1}", x, y));
                loggerService.Error(string.Format("Account : The LogOn model was invalid\r\nError keys were\r\n{0}", errors));
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }


        /// <summary>
        /// URL: /Account/LogOff
        /// </summary>
        public ActionResult LogOff()
        {
            formsService.SignOut();
            Session.Clear();
            loggerService.Info("Account : LogOff, Logging out and redirecting to Home");
            return RedirectToAction("Index", "Home");
        }


        /// <summary>
        /// URL: /Account/Register
        /// </summary>
        public ActionResult Register(string OpenID)
        {
            loggerService.Info("Account : Register, New user registration selected");
            ViewBag.PasswordLength = membershipService.MinPasswordLength;
            ViewBag.OpenID = OpenID;
            return View();
        }

        [HttpPost]
        [ValidateAntiForgeryToken(Salt = "Register")]
        public ActionResult Register(RegisterModel model)
        {
            if (ModelState.IsValid)
            {
                // Attempt to register the user
                MembershipCreateStatus createStatus = membershipService.CreateUser(model.UserName, model.Password, model.Email, model.OpenID);

                if (createStatus == MembershipCreateStatus.Success)
                {
                    formsService.SignIn(model.UserName, false /* createPersistentCookie */);
                    loggerService.Info("Account : Register, Success creating new user");
                    Session["EncryptedPasswordForUserToWriteDown"] = null;
                    return RedirectToAction("Index", "Home");
                }
                else
                {
                    string err = AccountValidation.ErrorCodeToString(createStatus);
                    loggerService.Error(string.Format("Account : Register, erroe {0}", err));
                    ModelState.AddModelError("", err);
                }
            }
            else
            {
                loggerService.Error("Account : Register, There were some error registering, possibly due to missing/incorrect registration settings being supplied");
            }

            // If we got this far, something failed, redisplay form
            ViewBag.PasswordLength = membershipService.MinPasswordLength;
            return View(model);
        }


        /// <summary>
        /// URL: /Account/ChangePassword
        /// </summary>
        [Authorize]
        public ActionResult ChangePassword()
        {

            loggerService.Info("Account : ChangePassword GET");
            ViewBag.PasswordLength = membershipService.MinPasswordLength;
            return View();
        }

        [Authorize]
        [HttpPost]
        [ValidateAntiForgeryToken(Salt = "ChangePassword")]
        public ActionResult ChangePassword(ChangePasswordModel model)
        {
            if (ModelState.IsValid)
            {
                if (membershipService.ChangePassword(User.Identity.Name, model.OldPassword, model.NewPassword))
                {
                    loggerService.Error("Account : Sucessfully changed password");
                    return RedirectToAction("ChangePasswordSuccess");
                }
                else
                {
                    loggerService.Error("Account : The current password is incorrect or the new password is invalid");
                    ModelState.AddModelError("", "The current password is incorrect or the new password is invalid.");
                }
            }
            else
            {
                string errors = ModelState.Keys.Aggregate((x, y) => string.Format("{0}\r\n{1}", x, y));
                loggerService.Error(string.Format("Account : ChangePassword model was invalid\r\nError keys were\r\n{0}", errors));
            }

            // If we got this far, something failed, redisplay form
            ViewBag.PasswordLength = membershipService.MinPasswordLength;
            return View(model);
        }


        /// <summary>
        /// URL: /Account/ChangePasswordSuccess
        /// </summary>
        public ActionResult ChangePasswordSuccess()
        {
            return View();
        }



        [ValidateInput(false)]
        public ActionResult Authenticate(string returnUrl)
        {
            var response = openid.GetResponse();
            if (response == null)
            {
                loggerService.Error("Account : OpenId response was NULL");
 
                //Let us submit the request to OpenID provider
                Identifier id;
                if (Identifier.TryParse(Request.Form["openid_identifier"], out id))
                {
                    try
                    {
                        var request = openid.CreateRequest(Request.Form["openid_identifier"]);


                        var claim = new ClaimsRequest
                        {
                            Email = DemandLevel.Require,
                            Nickname = DemandLevel.Require,
                            FullName = DemandLevel.Request,
                        };

                        var fetch = new FetchRequest();
                        fetch.Attributes.AddRequired(WellKnownAttributes.Name.First);
                        fetch.Attributes.AddRequired(WellKnownAttributes.Name.Last);

                        request.AddExtension(claim);
                        request.AddExtension(fetch);



                        return request.RedirectingResponse.AsActionResult();
                    }
                    catch (ProtocolException ex)
                    {
                        loggerService.Error(string.Format("Account : OpenId Protocol error {0}\r\n\r\n{1}", ex.Message, ex.StackTrace));

                        ViewBag.Message = ex.Message;
                        return View("LogOn");
                    }
                }

                ViewBag.Message = "Invalid identifier";
                return View("LogOn");
            }

            //Let us check the response
            switch (response.Status)
            {

                case AuthenticationStatus.Authenticated:
                    loggerService.Info("Account : OpenId authentication recieved");

                    LogOnModel lm = new LogOnModel();
                    lm.OpenID = response.ClaimedIdentifier;


                    var claim = response.GetExtension<ClaimsResponse>();
                    var fetch = response.GetExtension<FetchResponse>();
                    var nick = response.FriendlyIdentifierForDisplay;
                    var email = string.Empty;

                    if (claim != null)
                    {
                        nick = string.IsNullOrEmpty(claim.Nickname) ? claim.FullName : claim.Nickname;
                        email = claim.Email;
                    }

                    if (string.IsNullOrEmpty(nick) && fetch != null &&
                        fetch.Attributes.Contains(WellKnownAttributes.Name.First) &&
                        fetch.Attributes.Contains(WellKnownAttributes.Name.Last))
                    {
                        nick = fetch.GetAttributeValue(WellKnownAttributes.Name.First) + " " +
                               fetch.GetAttributeValue(WellKnownAttributes.Name.Last);
                    }




                    MembershipUser user = membershipService.GetUserByOpenId(lm.OpenID);
                    Tuple<MembershipCreateStatus, String> resultsAndPassword = null;

                    if (user == null)
                    {
                        resultsAndPassword = membershipService.CreateUser(nick, email, lm.OpenID);
                        Session["EncryptedPasswordForUserToWriteDown"] = resultsAndPassword.Item2;
                        user = membershipService.GetUserByOpenId(lm.OpenID);
                    }
                    else
                    {
                        Session["EncryptedPasswordForUserToWriteDown"] = user.GetPassword();
                    }

                    //check if user is still empty, which means we have now managed to authenticate via OpenId
                    //and store in database
                    if (user != null)
                    {
                        lm.UserName = user.UserName;
                        formsService.SignIn(user.UserName, false);
                        Session["User"] = user;
                        loggerService.Info("Account : redirecting to Home/Index");
                        return RedirectToAction("Index", "Home");
                    }
                    else
                    {
                        loggerService.Error("Account : redirecting to Home/Logon");
                        return View("LogOn", lm);
                    }

                case AuthenticationStatus.Canceled:
                    loggerService.Error("Account : OpenId issue : Canceled at provider");
                    ViewBag.Message = "Canceled at provider";
                    return View("LogOn");
                case AuthenticationStatus.Failed:
                    loggerService.Error(string.Format("Account : OpenId issue : {0}",response.Exception.Message));
                    ViewBag.Message = response.Exception.Message;
                    return View("LogOn");
            }

            return new EmptyResult();
        }


       

    }
}
